package net.qiujuer.library.clink.impl.async;

import net.qiujuer.library.clink.core.IoArgs;
import net.qiujuer.library.clink.core.SendDispatcher;
import net.qiujuer.library.clink.core.SendPacket;
import net.qiujuer.library.clink.core.Sender;
import net.qiujuer.library.clink.utils.CloseUtils;

import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class AsyncSendDispatcher implements SendDispatcher, IoArgs.IoArgsEventProcessor , AsyncPacketReader.PacketProvider {
    private final Sender sender;
    private final Queue<SendPacket> queue = new ConcurrentLinkedQueue<>();
    private final AtomicBoolean isClosed = new AtomicBoolean(false);
    // 发送过程当前有数据来的时候，我们要Packet丢到queue当中去，然后从queue当中把每一个Packet拿出来发送，而如果说当前正在
    // 进行一个发送状态。当咋们没有数据发送的时候，也就是说我们不再去监听发送的空闲状态，这个时候我们的发送状态是一个关闭状态
    // 知道有新数据来的时候，我们会重新去激活这个状态，所以我们需要一个变量来维护这个状态。
    private final AtomicBoolean isSending = new AtomicBoolean();

    private final AsyncPacketReader reader = new AsyncPacketReader(this);
    private final Object queueLock = new Object();

    public AsyncSendDispatcher(Sender sender) {
        this.sender = sender;
        sender.setSendListener(this);
    }

    @Override
    public void send(SendPacket packet) {
        synchronized (queueLock) {
            queue.offer(packet);
            // 如果当前不在发送中，就设置为发送中
            if (isSending.compareAndSet(false,true)){
                /**
                 * 调用reader请求拿一个Packet，如果说拿成功了，接下来要做的事情是注册一次说我的网络需要进行数据发送
                 * 注册网络数据发送是由我来完成的，具体拿数据是由reader来做的事情
                 */
                if (reader.requestTakePacket()) {
                    requestSend();
                }
            }
        }
    }

    @Override
    public void cancel(SendPacket packet) {
        boolean ret ;
        synchronized (queueLock) {
            ret = queue.remove(packet);
        }
        if (ret) {
            packet.cancel();
            return;
        }

        reader.cancel(packet);
    }

    @Override
    public SendPacket takePacket() {
        SendPacket packet;
        synchronized (queueLock) {
            packet = queue.poll();
            if (packet == null) {
                // 队列为空，取消发送状态
                isSending.set(false);
                return null;
            }
        }

        if (packet.isCanceled()) {
            // 已取消，不用发送，递归拿下一条
            return takePacket();
        }
        return packet;
    }

    /**
     * 完成Packet发送
     * @param isSucceed 是否成功
     */
    @Override
    public void completedPacket(SendPacket packet, boolean isSucceed) {
        CloseUtils.close(packet);
    }

    /**
     * 请求网络进行数据发送
     */
    private void requestSend() {
        try {
            sender.postSendASync();
        } catch (IOException e) {
            closeAndNotify();
        }

    }

    private void closeAndNotify() {
        CloseUtils.close(this);
    }

    @Override
    public void close() throws IOException {
        if (isClosed.compareAndSet(false, true)) {
            isSending.set(false);
            // reader的关闭操作
            reader.close();
        }
    }

    @Override
    public IoArgs provideIoArgs() {
        return reader.fillData();
    }

    @Override
    public void onConsumeFailed(IoArgs args, Exception e) {
        if (args != null) {
            e.printStackTrace();
        }else {
            // TODO
        }
    }

    @Override
    public void onConsumeCompleted(IoArgs args) {
        // 继续发送当前包
        if (reader.requestTakePacket()) {
            requestSend();
        }
    }
}
